package com.example.func.aspect;

import com.example.func.annotation.RequestLog;
import com.example.func.enums.ModuleType;
import com.example.func.enums.Scenario;
import com.fasterxml.jackson.databind.ObjectMapper;
import cn.hutool.extra.spring.SpringUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;

/**
 * 请求日志切面
 * 用于记录带有@RequestLog注解的方法的请求参数
 */
@Aspect
@Component
public class RequestLogAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(RequestLogAspect.class);
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    /**
     * 在方法执行前记录请求参数
     */
    @Before("@annotation(com.example.func.annotation.RequestLog)")
    public void logRequest(JoinPoint joinPoint) {
        try {
            // 获取方法签名和注解信息
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            RequestLog requestLog = method.getAnnotation(RequestLog.class);
            
            // 解析场景、模块与 serviceName
            Scenario scenario = requestLog.scenario();
            String module;
            String serviceName;
            if (scenario != null && scenario != Scenario.NONE) {
                ModuleType moduleType = scenario.getModuleType();
                module = moduleType != null && moduleType != ModuleType.DEFAULT
                        ? moduleType.name()
                        : method.getDeclaringClass().getSimpleName();
                serviceName = scenario.getServiceName();
            } else {
                // 兼容旧字段：module
                module = !requestLog.module().isEmpty() ? requestLog.module() : method.getDeclaringClass().getSimpleName();
                serviceName = requestLog.serviceName();
            }

            // 若配置了 serviceName，则尝试从容器中获取对应 Bean（Hutool SpringUtil）
            Object serviceBean = null;
            if (serviceName != null && !serviceName.isEmpty()) {
                try {
                    serviceBean = SpringUtil.getBean(serviceName);
                } catch (Exception e) {
                    logger.warn("未找到名为 {} 的 Bean", serviceName);
                }
            }
            
            // 获取HTTP请求信息
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                logger.warn("无法获取HTTP请求信息，跳过日志记录");
                return;
            }
            
            HttpServletRequest request = attributes.getRequest();
            
            // 构建日志信息
            StringBuilder logBuilder = new StringBuilder();
            logBuilder.append("\n========== 请求日志开始 ==========\n");
            logBuilder.append("模块: ").append(module).append("\n");
            logBuilder.append("方法: ").append(method.getName()).append("\n");
            if (serviceBean != null) {
                logBuilder.append("serviceName: ").append(serviceName)
                          .append(" (beanClass: ").append(serviceBean.getClass().getName()).append(")\n");
                // 动态调用 service 的 getUserById 方法
                try {
                    // 解析 id 参数（优先从请求参数中取 id 或 userId）
                    Long resolvedId = null;
                    String idStr = request.getParameter("id");
                    if (idStr == null || idStr.isEmpty()) {
                        idStr = request.getParameter("userId");
                    }
                    if (idStr != null && !idStr.isEmpty()) {
                        try {
                            resolvedId = Long.valueOf(idStr);
                        } catch (NumberFormatException ignore) {
                            // 忽略格式错误，尝试从方法参数中查找
                        }
                    }

                    // 若请求参数未取到，则尝试从方法实参中寻找 Long/Number 类型
                    Object[] args = joinPoint.getArgs();
                    if (resolvedId == null && args != null) {
                        for (Object a : args) {
                            if (a instanceof Long) {
                                resolvedId = (Long) a;
                                break;
                            } else if (a instanceof Number) {
                                resolvedId = ((Number) a).longValue();
                            break;
                            }
                        }
                    }

                    if (resolvedId != null) {
                        Object invokeResult = null;
                        try {
                            java.lang.reflect.Method m = serviceBean.getClass().getMethod("getUserById", Long.class);
                            invokeResult = m.invoke(serviceBean, resolvedId);
                        } catch (NoSuchMethodException e1) {
                            try {
                                java.lang.reflect.Method m2 = serviceBean.getClass().getMethod("getUserById", long.class);
                                invokeResult = m2.invoke(serviceBean, resolvedId.longValue());
                            } catch (NoSuchMethodException e2) {
                                logger.warn("{} 未定义 getUserById(Long) 方法", serviceBean.getClass().getName());
                            }
                        }

                        if (invokeResult != null) {
                            try {
                                String resultJson = objectMapper.writeValueAsString(invokeResult);
                                logBuilder.append("getUserById(").append(resolvedId).append(") -> 修改前值").append(resultJson).append("\n");
                            } catch (Exception jsonEx) {
                                logBuilder.append("getUserById(").append(resolvedId).append(") -> ")
                                          .append(invokeResult.toString()).append("\n");
                            }
                        }
                    }
                } catch (Exception reflectEx) {
                    logger.warn("调用 getUserById 反射时发生异常: {}", reflectEx.getMessage());
                }
            }
            logBuilder.append("请求URL: ").append(request.getRequestURL().toString()).append("\n");
            logBuilder.append("请求方法: ").append(request.getMethod()).append("\n");
            logBuilder.append("客户端IP: ").append(getClientIpAddress(request)).append("\n");
            
            // 记录请求头
            logBuilder.append("请求头:\n");
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                String headerValue = request.getHeader(headerName);
                logBuilder.append("  ").append(headerName).append(": ").append(headerValue).append("\n");
            }
            
            // 记录请求参数
            logBuilder.append("请求参数:\n");
            
            // GET参数
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (!parameterMap.isEmpty()) {
                logBuilder.append("  GET参数:\n");
                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    String key = entry.getKey();
                    String[] values = entry.getValue();
                    logBuilder.append("    ").append(key).append(": ").append(Arrays.toString(values)).append("\n");
                }
            }
            
            // 方法参数
            Object[] args = joinPoint.getArgs();
            String[] paramNames = signature.getParameterNames();
            if (args != null && args.length > 0) {
                logBuilder.append("  方法参数:\n");
                for (int i = 0; i < args.length; i++) {
                    String paramName = i < paramNames.length ? paramNames[i] : "arg" + i;
                    Object arg = args[i];
                    try {
                        String argJson = objectMapper.writeValueAsString(arg);
                        logBuilder.append("    ").append(paramName).append(": ").append(argJson).append("\n");
                    } catch (Exception e) {
                        logBuilder.append("    ").append(paramName).append(": 修改后值").append(arg != null ? arg.toString() : "null").append("\n");
                    }
                }
            }
            
            logBuilder.append("========== 请求日志结束 ==========");
            
            // 输出日志
            logger.info(logBuilder.toString());
            
        } catch (Exception e) {
            logger.error("记录请求日志时发生错误", e);
        }
    }

    /**
     * 在方法成功返回后执行后置逻辑
     */
    @AfterReturning(pointcut = "@annotation(com.example.func.annotation.RequestLog)", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();

            StringBuilder logBuilder = new StringBuilder();
            logBuilder.append("\n========== 后置返回日志 ==========" + "\n");
            logBuilder.append("方法: ").append(method.getName()).append("\n");

            try {
                String resultJson = objectMapper.writeValueAsString(result);
                logBuilder.append("返回值: ").append(resultJson).append("\n");
            } catch (Exception jsonEx) {
                logBuilder.append("返回值: ").append(result != null ? result.toString() : "null").append("\n");
            }

            logBuilder.append("========== 后置返回结束 ==========");
            logger.info(logBuilder.toString());
        } catch (Exception e) {
            logger.error("后置返回日志记录时发生错误", e);
        }
    }
    
    /**
     * 获取客户端真实IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
}
